package com.lonely.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lonely.common.constant.UserConstants;
import com.lonely.common.core.domain.BaseEntity;
import com.lonely.common.core.page.Condition;
import com.lonely.common.enums.CommonStatus;
import com.lonely.common.exception.BusinessException;
import com.lonely.common.utils.SecurityUtils;
import com.lonely.common.utils.StringUtils;
import com.lonely.system.domain.dto.menu.BatchDeleteMenuDTO;
import com.lonely.system.domain.dto.menu.MenuSubmitDTO;
import com.lonely.system.domain.dto.menu.QueryButtonDto;
import com.lonely.system.domain.dto.menu.SysMenuPageQueryDto;
import com.lonely.system.domain.entity.SysMenu;
import com.lonely.system.domain.vo.system.menu.QueryRoleButtonVo;
import com.lonely.system.domain.vo.system.menu.SysMenuPageQueryVo;
import com.lonely.system.domain.vo.system.menu.SysMenuTreeVo;
import com.lonely.system.mapper.SysMenuMapper;
import com.lonely.system.service.ISysMenuService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 菜单 业务层处理
 *
 * @author fzzf
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    /**
     * 菜单分页查询列表
     *
     * @param queryDto
     * @return
     */
    @Override
    public Page<SysMenuPageQueryVo> page(SysMenuPageQueryDto queryDto) {
        Page<SysMenu> page = Condition.page(queryDto);
        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<SysMenu>()
                .like(SysMenu::getMenuName, queryDto.getMenuName());
        Page<SysMenu> menuPage = super.page(page, queryWrapper);

        if (CollUtil.isEmpty(menuPage.getRecords())) {
            return new Page<>();
        }

        // 转换成树形结构
        List<SysMenuPageQueryVo> pageQueryVos = menuPage.getRecords().stream().map(menu -> {
            SysMenuPageQueryVo userPageQueryVo = new SysMenuPageQueryVo();
            BeanUtils.copyProperties(menu, userPageQueryVo);
            userPageQueryVo.setId(menu.getId());
            userPageQueryVo.setCreateTime(DateUtil.formatDateTime(menu.getCreateTime()));
            return userPageQueryVo;
        }).collect(Collectors.toList());

        Page<SysMenuPageQueryVo> resultPage = new Page<>();
        BeanUtils.copyProperties(menuPage, resultPage);
//        resultPage.setRecords(recursionGeneratorMenuPageTree(pageQueryVos, 0L));
        resultPage.setRecords(buildTreeFromList(pageQueryVos));
        return resultPage;
    }

    /**
     * 从一个扁平列表中构建树形结构（仅基于 parentId 和 id 的关系）
     * 适用于：搜索结果内部存在父子关系时自动成树，否则保持平级
     *
     * @param list 查询结果的菜单 VO 列表
     * @return 构建后的树形结构（根节点列表）
     */
    private List<SysMenuPageQueryVo> buildTreeFromList(List<SysMenuPageQueryVo> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }

        // 用 Map 加速查找
        Map<Long, SysMenuPageQueryVo> idMap = list.stream()
                .collect(Collectors.toMap(SysMenuPageQueryVo::getId, Function.identity()));

        // 标记哪些节点是“子节点”（即它的 parentId 在结果集中存在）
        Set<Long> childNodeIds = new HashSet<>();
        for (SysMenuPageQueryVo menu : list) {
            Long parentId = menu.getParentId();
            if (parentId != null && !parentId.equals(0L)) {
                if (idMap.containsKey(parentId)) {
                    childNodeIds.add(menu.getId());
                }
            }
        }

        // 所有“不是子节点”的节点作为根节点
        List<SysMenuPageQueryVo> rootList = list.stream()
                .filter(menu -> !childNodeIds.contains(menu.getId()))
                .collect(Collectors.toList());

        // 构建 children 关系
        for (SysMenuPageQueryVo menu : list) {
            Long menuId = menu.getId();
            Long parentId = menu.getParentId();

            // 如果当前菜单的父菜单也在结果集中，则挂载
            if (parentId != null && !parentId.equals(0L)) {
                SysMenuPageQueryVo parent = idMap.get(parentId);
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(menu);
                }
            }
        }

        // 注意：上面循环已经建立了 children，但要确保每个节点的 children 非 null
        for (SysMenuPageQueryVo menu : list) {
            if (menu.getChildren() == null) {
                menu.setChildren(new ArrayList<>());
            }
        }

        return rootList;
    }

    /**
     * 获取菜单树
     *
     * @return
     */
    @Override
    public List<SysMenuTreeVo> getMenuTree() {
        // 查询最顶级的菜单
        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getParentId, 0L)
                .eq(SysMenu::getStatus,CommonStatus.ENABLE.code);
        List<SysMenu> outerLayerMenus = super.list(queryWrapper);
        if (CollUtil.isEmpty(outerLayerMenus)) {
            return new ArrayList<>();
        }

        // 获取当前菜单的所有子菜单(不要按钮的)
        List<SysMenu> allMenus = new ArrayList<>(outerLayerMenus);
        for (SysMenu currentMenu : outerLayerMenus) {
            allMenus.addAll(super.list(new LambdaQueryWrapper<SysMenu>()
                    .ne(SysMenu::getMenuType, UserConstants.TYPE_BUTTON)
                    .likeRight(SysMenu::getAncestors, String.format("%s,", currentMenu.getAncestors()))
                    .eq(SysMenu::getStatus,CommonStatus.ENABLE.code)));
        }

        // 转换成树形结构
        List<SysMenuTreeVo> menuTreeVos = allMenus.stream().map(menu -> {
            SysMenuTreeVo menuTreeVo = new SysMenuTreeVo();
            menuTreeVo.setId(menu.getId());
            menuTreeVo.setPId(menu.getParentId());
            menuTreeVo.setLabel(menu.getMenuName());
            menuTreeVo.setIsLeaf(Objects.equals(UserConstants.TYPE_MENU, menu.getMenuType()));
            return menuTreeVo;
        }).collect(Collectors.toList());
        return recursionGeneratorMenuTree(menuTreeVos, 0L);
    }

    /**
     * 查询指定菜单下的按钮集合
     *
     * @param queryButtonDto
     * @return
     */
    @Override
    public List<QueryRoleButtonVo> getButtonsByMenuIds(QueryButtonDto queryButtonDto) {

        if (CollUtil.isEmpty(queryButtonDto.getMenuIds())) {
            return new ArrayList<>();
        }

        return Optional.ofNullable(this.list(new LambdaQueryWrapper<SysMenu>()
                .in(SysMenu::getParentId, queryButtonDto.getMenuIds())
                .eq(SysMenu::getMenuType, UserConstants.TYPE_BUTTON)
                .eq(SysMenu::getStatus, CommonStatus.ENABLE.code)
                .orderByAsc(SysMenu::getId)
        )).orElse(new ArrayList<>()).stream().map(x -> {
            QueryRoleButtonVo queryRoleButtonVo = new QueryRoleButtonVo();
            queryRoleButtonVo.setButtonId(x.getId());
            queryRoleButtonVo.setButtonName(x.getMenuName());
            return queryRoleButtonVo;
        }).collect(Collectors.toList());
    }


    /**
     * 递归构建菜单树
     *
     * @param allMenus
     * @param parentMenuId
     * @return
     */
    private List<SysMenuTreeVo> recursionGeneratorMenuTree(List<SysMenuTreeVo> allMenus, Long parentMenuId) {
        List<SysMenuTreeVo> menus = allMenus.stream().filter(menu -> Objects.equals(menu.getPId(), parentMenuId)).collect(Collectors.toList());
        if (CollUtil.isEmpty(menus)) {
            return null;
        }

        for (SysMenuTreeVo currentMenu : menus) {
            currentMenu.setChildren(recursionGeneratorMenuTree(allMenus, currentMenu.getId()));
        }
        return menus;
    }

    /**
     * 根据用户ID查询按钮权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    @Override
    public Set<String> selectButtonPermsByUserId(Long userId) {
        List<String> perms = super.getBaseMapper().selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (StringUtils.isNotEmpty(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 查询指定用户关联的菜单path
     *
     * @param userId
     * @return
     */
    @Override
    public Set<String> selectMenuPathByUserId(Long userId) {
        return super.getBaseMapper().selectMenuPathByUserId(userId);
    }

    /**
     * 保存/编辑 菜单
     *
     * @param submitDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submit(MenuSubmitDTO submitDTO) {
        this.checkMenu(submitDTO);

        // 获取上级菜单信息
        Long parentId = ObjectUtil.defaultIfNull(submitDTO.getParentId(), 0L);
        String parentAncestors;
        if (parentId != 0L) {
            SysMenu parentMenu = super.getById(parentId);
            Assert.notNull(parentMenu, "上级菜单不存在");
            parentAncestors = parentMenu.getAncestors();
        } else {
            parentAncestors = "0";
        }

        // 判断是新增还是修改
        if (submitDTO.getId() == null) {
            SysMenu sysMenu = new SysMenu();
            BeanUtils.copyProperties(submitDTO, sysMenu);
            sysMenu.setParentId(ObjectUtil.defaultIfNull(submitDTO.getParentId(), 0L));
            sysMenu.setCreateBy(SecurityUtils.getUserId());
            sysMenu.setUpdateBy(SecurityUtils.getUserId());
            super.save(sysMenu);

            // 更新父子链
            sysMenu.setAncestors(String.format("%s,%s", parentAncestors, sysMenu.getId()));
            super.updateById(sysMenu);
        } else {
            // 更新数据
            SysMenu oldMenu = super.getById(submitDTO.getId());
            Assert.notNull(oldMenu, "菜单不存在");

            BeanUtils.copyProperties(submitDTO, oldMenu);
            oldMenu.setUpdateBy(SecurityUtils.getUserId());
            super.updateById(oldMenu);
        }
    }

    /**
     * 删除菜单
     *
     * @param menuId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenu(Long menuId) {
        SysMenu menu = super.getById(menuId);
        Assert.notNull(menu, "菜单不存在");

        // 判断菜单类型，如果是目录，则需要判断是否有子菜单
        if (Objects.equals(menu.getMenuType(), UserConstants.TYPE_DIR)) {
            int count = super.count(new LambdaQueryWrapper<SysMenu>()
                    .eq(SysMenu::getParentId, menuId)
            );
            Assert.isTrue(count == 0, "存在子菜单，不允许删除");
        }

        // 删除菜单
        super.remove(new LambdaQueryWrapper<SysMenu>()
                .likeRight(SysMenu::getAncestors, menu.getAncestors())
        );
    }

    /**
     * 批量删除菜单
     * TODO：该实现有问题，暂不使用。
     *
     * @param deleteMenuDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Deprecated
    public void batchDeleteMenu(BatchDeleteMenuDTO deleteMenuDTO) {
        List<SysMenu> sysMenus = super.listByIds(deleteMenuDTO.getMenuIds());
        if (CollUtil.isEmpty(sysMenus)) {
            throw new BusinessException("没有待删除的菜单");
        }

        List<Long> dirMenuIds = sysMenus.stream().filter(x -> Objects.equals(x.getMenuType(), UserConstants.TYPE_DIR))
                .map(BaseEntity::getId)
                .distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(dirMenuIds)) {
            Map<Long, Long> menuChildSizeMap = super.list(new LambdaQueryWrapper<SysMenu>()
                    .in(SysMenu::getParentId, dirMenuIds)
            ).stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.counting()));
            dirMenuIds.forEach(x -> {
                Long count = menuChildSizeMap.get(x);
                Assert.isTrue(count == 0, "存在子菜单，不允许删除");
            });
        }

        // 删除菜单
        super.removeByIds(sysMenus.stream().map(BaseEntity::getId).collect(Collectors.toList()));
    }

    /**
     * 校验提交的菜单信息
     *
     * @param submitDTO
     */
    private void checkMenu(MenuSubmitDTO submitDTO) {
        // 1. 校验菜单名称是否重复
        Long parentId = ObjectUtil.defaultIfNull(submitDTO.getParentId(), 0L);
        int count = super.count(new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getParentId, parentId)
                .eq(SysMenu::getMenuName, submitDTO.getMenuName())
                .ne(submitDTO.getId() != null, SysMenu::getId, submitDTO.getId())
        );
        Assert.isTrue(count == 0, "菜单名称已存在");

    }

}
